package atm.zhaz.server;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Pattern;

public class Server {
	static final Pattern REQUEST_PATTERN = Pattern
			.compile("~[a-zA-Z]+:[a-zA-Z]+:[a-zA-Z]+:[a-zA-Z0-9=;.]*~");
	static final String RESPONSE_FORMAT = "~%s:%s~";

	static ServerSocket serverSocket;
	static Map<String, Socket> clientMap = new HashMap<String, Socket>();
	static Map<String, User> userMap = new HashMap<String, User>();
	static Thread clientListener;
	static boolean done = false;
	static String bindAddress = "";
	static int listenPort = 12345;

	public static void main(String[] args) {
		File f = new File("server.txt");
		if (f.exists()) {
			loadFromFile(f);
		}
		if (args.length == 4 && args[0].equals("-c")) {
			User u = new User();
			u.setUsername(args[1]);
			u.setPassword(args[2]);
			u.setMoney(new Long(args[3]));
			saveUser(u);
		} else {
			if (args.length == 1) {
				listenPort = Integer.parseInt(args[0]);
			}
			try {
				serverSocket = new ServerSocket(listenPort);
				clientListener = new Thread(new ClientListener());
				clientListener.start();
				while (!done) {
					int q = System.in.read();
					if ((char) q == 'q') {
						System.out.println("CLOSING SERVER");
						done = true;
					} else if ((char) q == 'c') {
						System.out
								.println("Input <username> <password> <money>: ");

						try {
							BufferedReader in = new BufferedReader(
									new InputStreamReader(System.in));
							in.readLine();
							String line = in.readLine();
							String[] r = line.split(" ");
							String username = r[0];
							String password = r[1];
							Long money = Long.valueOf(0);
							try {
								money = Long.parseLong(r[2]);
							} catch (Exception e) {
								e.printStackTrace();
							}
							User u = new User();
							u.setUsername(username);
							u.setPassword(password);
							u.setMoney(money);
							System.out.println(u);
							saveUser(u);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
					System.out.println("read");
				}
				serverSocket.close();
				System.out.println("WAIT while CLIENT LISTENER TERMINTATES");
				while (!clientListener.getState().equals(
						Thread.State.TERMINATED)) {
					Thread.sleep(100);
				}
				System.out.println("QUIT");
			} catch (Exception e) {
				System.out.println(e.toString());
			}
		}
		saveToFile(f);
	}

	public static void loadFromFile(File f) {
		try {
			Scanner in = new Scanner(f);
			while (in.hasNextLine()) {
				String line = in.nextLine();
				String [] v = line.split(";");
				if (v.length != 3){
					//invalid
					continue;
				}
				User u = new User();
				u.setUsername(v[0]);
				u.setPassword(v[1]);
				u.setMoney(new Long(v[2]));
				System.out.println("Load: " + u.toString());
				saveUser(u);
			}
			in.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void saveToFile(File f) {
		try {
			PrintWriter out = new PrintWriter(f);
			for (User u : userMap.values()) {
				out.println(String.format("%s;%s;%d;", u.getUsername(), u
						.getPassword(), u.getMoney()));
				System.out.println("Save: " + u.toString());
			}
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	public static void saveUser(User user) {
		userMap.put(user.getUsername(), user);
	}

	public static User getUser(String username) {
		User u = userMap.get(username);
		System.out.println("GET: " + username + " : " + u.toString());
		return u;
	}

	public static void speak_with_client(Scanner in, PrintWriter out) {
		System.out.println("SPEAK");
		String username = "";
		String password = "";
		String operation = "";
		Long amount = Long.valueOf(0);
		User to = null;
		String response = "";

		// get request
		String line = in.findInLine(REQUEST_PATTERN);
		if (line != null) {
			line = line.substring(1, line.length() - 1);
			System.out.println("request: " + line);
			String[] r = line.split(":");
			if (r.length != 4) {
				System.out.println("size of request line is suspicious: "
						+ r.length);
			}
			username = r[0];
			password = r[1];
			operation = r[2];
			if (r.length > 3 && r[3] != null && r[3].length() > 0) {
				String[] mr = r[3].split(";");
				int k = mr.length;
				for (int i = 0; i < k; i++) {
					String[] kv = mr[i].split("=");
					if (kv[0].equals("AMOUNT")) {
						amount = Long.parseLong(kv[1]);
					} else if (kv[0].equals("TO")) {
						to = getUser(kv[1]);
					}
				}
			}
			// answer
			User user = getUser(username);
			if (user != null && user.getPassword().equals(password)) {
				if (operation.equals("AUTH")) {
					response = String.format(RESPONSE_FORMAT, "OK", ""
							+ user.getMoney());
				} else if (operation.equals("BALANCE")) {
					response = String.format(RESPONSE_FORMAT, "OK", ""
							+ user.getMoney());
				} else if (operation.equals("WITHDRAW")) {
					if (user.getMoney().compareTo(amount) > 0) {
						user.setMoney(user.getMoney() - amount);
						saveUser(user);
						response = String.format(RESPONSE_FORMAT, "OK", ""
								+ user.getMoney());
					} else {
						response = String.format(RESPONSE_FORMAT, "NOPOSSIBLE",
								"not enough money");

					}

				} else if (operation.equals("SEND")) {
					if (user.getMoney().compareTo(amount) > 0) {
						if (to != null) {
							user.setMoney(user.getMoney() - amount);
							to.setMoney(to.getMoney() + amount);
							saveUser(user);
							saveUser(to);
							response = String.format(RESPONSE_FORMAT, "OK", ""
									+ user.getMoney());
						} else {
							response = String.format(RESPONSE_FORMAT,
									"INVALID", "not such user");
						}
					} else {
						response = String.format(RESPONSE_FORMAT, "NOPOSSIBLE",
								"not enough money");
					}

				} else if (operation.equals("DEPOSIT")) {
					user.setMoney(user.getMoney() + amount);
					saveUser(user);
					response = String.format(RESPONSE_FORMAT, "OK", ""
							+ user.getMoney());
				}
			} else {
				response = String.format(RESPONSE_FORMAT, "INVALID",
						"no such user");
			}
		} else {
			response = String.format(RESPONSE_FORMAT, "FAIL", "REQUEST FAIL");
		}
		System.out.println(response);
		out.println(response);
		out.flush();

	}

	public static class ClientListener implements Runnable {
		List<Thread> serveList = new ArrayList<Thread>();

		@Override
		public void run() {
			while (!done) {
				try {
					for (Thread t : serveList) {
						if (t.getState().equals(Thread.State.TERMINATED)) {
							serveList.remove(t);
						}
					}
					System.out.println("listen");
					Socket tmp = serverSocket.accept();
					String key = tmp.getRemoteSocketAddress().toString();
					clientMap.put(key, tmp);
					Thread tmpServe = new Thread(new Serve(tmp));
					serveList.add(tmpServe);
					tmpServe.start();
					System.out.println(String.format("new client: %s", key));
				} catch (Exception e) {
					System.out.println("connection error");
					System.out.println(e.toString());
				}
			}
			if (serveList.size() != 0)
				System.out.println("WAIT while SERVE Threads TERMINTATES");
			while (serveList.size() != 0) {
				for (Thread t : serveList) {
					if (t.getState().equals(Thread.State.TERMINATED)) {
						serveList.remove(t);
					}
				}
			}
		}

	}

	public static class Serve implements Runnable {
		Socket client;

		public Serve() {
		}

		public Serve(Socket s) {
			client = s;
		}

		@Override
		public void run() {
			try {
				Scanner in = new Scanner(client.getInputStream());
				PrintWriter out = new PrintWriter(client.getOutputStream());
				speak_with_client(in, out);
				in.close();
				out.close();
				client.close();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
			}
		}

	}

}
